The role of cryptography in computer software has become increasingly critical in recent years. In the past, the use of cryptography was limited to specific types of application but more recent concerns with privacy as well as security have forced application developers to respond with a significantly increased use of cryptography across a wide range of applications.
Unfortunately, this increase in the use of cryptography (as well as increases in other areas of security) has not yielded a significant increase in privacy or security in practice. One of the reasons for this is that standard cryptography algorithms are based on a premise that does not always exist in the real world.
A fundamental premise underlying all standard cryptography is that the secrets (such as keys) used to perform cryptographic operations are kept secret. If the secrets are exposed, then any information protected using those secrets is also exposed. In cryptographic literature, the entities involved in information exchange are modelled as opaque endpoints, such that if an entity makes use of a secret, then it is assumed that this secret remains private to that entity unless it transmits information that allows another entity to derive the secret.
In the real world, however, these entities are typically applications running on devices that may be subject to malware attacks or even direct access by malicious individuals. If an attacker has direct access to the software running on a device, then it is usually trivial for them to steal any secrets being used by cryptographic operations within those applications. When this occurs, the premise described above no longer holds and the affected application will no longer be gaining any benefit from the use of cryptography.
This problem has given rise in recent years to a new field called White Box Cryptography, which aims to solve the problem by re-implementing cryptographic operations in a form that prevents these secrets from being extracted directly from the application. At a minimum, a whitebox version of a cryptographic operation should prevent the secret data from being exposed in memory, even for a very short period of time. In practice, however, it also needs to resist attacks that aim to derive the secret data by examining the internals of the implementation.
Early attempts at producing whitebox implementations were highly algorithm-specific—the methodology used for each algorithm had to be carefully designed by an expert with intimate knowledge of the algorithm. They were very effective at preventing the secret data from being exposed in memory but far less effective at preventing its derivation through examination of the implementation's internals. The primary reason for this is that most cryptographic algorithms rely on the complexity of the operation as a whole—when viewed at a more granular level, this complexity is no longer present and secrets can often be derived by observing the flow of data through a relatively small part of the overall algorithm.
For example, the Advanced Encryption Standard (AES) algorithm performs the same sequence of steps multiple times (each time is called a round) and its strength relies on using 10, 12 or 14 rounds (depending on key size). If an attacker can observe the inputs and outputs for the individual rounds, they will be able to recover the key far more easily than if they were observing all of the rounds as a single, opaque unit.
More recent solutions have attempted to solve this problem by implementing the whitebox version of the algorithm using a heavily-obfuscated form in which the program and data flow are extremely difficult to follow and understand, either by a human or an automated process. Some of these solutions have proven to be very effective in practice, at least in terms of preventing key theft.
However, one problem that they have not significantly reduced is the effort required to support new algorithms. This remains a relatively manual process that requires significant expertise. This may not appear to be a serious issue given the limited number of algorithms in common use, and indeed, it is not an issue for use cases that require only static keys, but it does pose a problem for those that require dynamic keys.
A static key is a key that is known when the whitebox implementation is generated, and is a fundamental element of whitebox solutions—the process of creating a whitebox essentially involves embedding a static key into a version of the cryptographic algorithm in a form that makes it difficult to derive the key's value. A dynamic key is a key which is only known at runtime, which implies that it is passed to the whitebox instead of already being embedded within it, which goes against the inherently static nature of the whitebox.
To accommodate dynamic keys, a simple solution is to encrypt keys using another key (and possibly a different algorithm), where this other key becomes the static key embedded in the whitebox. The whitebox is then used to decrypt the real key, which is then used with a standard algorithm to perform the originally-intended operation.
The problem with this simple solution is that the real key will be exposed in memory when it is decrypted, which partially defeats the purpose of using a whitebox in the first place. Existing solutions have attempted to reduce this problem by combining the sequence of operations and obfuscating the result with a general-purpose obfuscation mechanism in order to make it more difficult to isolate the point at which the real key is exposed. This represents a significant compromise on the effectiveness of such solutions when applied to dynamic keys.
Another major issue with existing solutions is that they do not address a specific class of attacks on whitebox implementations which are commonly known as lifting attacks although a more accurate name would be out-of-context attack. An out-of-context attack is an attack in which the whitebox implementation is used by an attacker to perform the cryptographic operation on their behalf, without having to ever know the value of the key. Without specific defences against this type of attack, in practice, a whitebox implementation may provide no more security than a standard implementation.
In order to provide sufficient protection of cryptographic operations in the real world, it is therefore necessary to have whitebox implementations that support dynamic keys without significantly weakening the protection, and that prevent lifting attacks.